home *** CD-ROM | disk | FTP | other *** search
- /*
- * Z_Buffer.C - zBuffer implementation for concave polygons.
- *
- * Copyright (C) 1992, Christoph Streit (streit@iam.unibe.ch)
- * University of Berne, Switzerland
- * All rights reserved.
- *
- * This software may be freely copied, modified, and redistributed
- * provided that this copyright notice is preserved on all copies.
- *
- * You may not distribute this software, in whole or in part, as part of
- * any commercial product without the express consent of the authors.
- *
- * There is no warranty or other guarantee of fitness of this software
- * for any purpose. It is provided solely "as is".
- *
- */
-
- #ifdef AMIGA_GCC
- #include "string.h"
- #endif
-
- #include <values.h>
- #include "Z_Buffer.h"
-
- //___________________________________________________________ EdgeElement
-
- struct EdgeElement {
- real x;
- real z;
- };
-
- declareList(EdgeList, EdgeElement);
- implementList(EdgeList, EdgeElement);
-
- //___________________________________________________________ Z_Buffer
-
- Z_Buffer::Z_Buffer(ViewTransform* v, const rcString& msg, const rcString& oname)
- : view(v), remark(msg), ymin(resY), ymax(0), numPrimitives(0)
- {
- resY = view->getResY();
- resX = view->getResX();
-
- /*
- * Create the yBuckets.
- */
- yBuckets = new EdgeList*[resY];
- for (register int i=0; i<resY; i++)
- yBuckets[i] = new EdgeList(2);
-
- /*
- * Create and initialize z-Buffer array.
- */
- zBuffer = new float[resY*resX];
- for (i=0; i<resY*resX; i++)
- zBuffer[i] = MAXFLOAT;
-
- /*
- * Create and initialize pixmap.
- */
- pixmap = new unsigned char[resY*resX*3];
- memset(pixmap, 0, resY*resX*3);
-
- /*
- * Attach stream to outfile.
- */
- if (oname == "cout")
- outfile = stdout;
- else
- outfile = fopen((const char*) oname, "w");
-
- if (outfile == NULL)
- Error(ERR_PANIC, "can't open file " + oname);
- }
-
- Z_Buffer::~Z_Buffer()
- {
- delete view;
- delete [] zBuffer;
- delete [] pixmap;
- for (register int i=0; i<resY; i++)
- delete yBuckets[i];
- delete [] yBuckets;
- }
-
- void Z_Buffer::renderTriangle(const Color& color, const Vector& p1,
- const Vector& p2, const Vector& p3)
- {
- numPrimitives++;
-
- Vector tp1 = view->transformWorld2View(p1);
- Vector tp2 = view->transformWorld2View(p2);
- Vector tp3 = view->transformWorld2View(p3);
-
- /*
- * Front plane clipping (z=0).
- */
- if (tp1[2] < 0 || tp2[2] < 0 || tp3[2] < 0)
- return;
-
- Vector normal = (tp2-tp1)*(tp3-tp1);
- if (!normal.normalize())
- return;
-
- Vector lightVector = (tp1+tp2+tp3)/3;
- calculateColor(color, normal, lightVector);
-
- tp1 = view->transformView2Screen(tp1);
- tp2 = view->transformView2Screen(tp2);
- tp3 = view->transformView2Screen(tp3);
-
- addEdge(tp1, tp2);
- addEdge(tp2, tp3);
- addEdge(tp3, tp1);
-
- render();
- }
-
- void Z_Buffer::renderRectangle(const Color& color, const Vector& p1, const Vector& p2,
- const Vector& p3, const Vector& p4)
- {
- numPrimitives++;
-
- Vector tp1 = view->transformWorld2View(p1);
- Vector tp2 = view->transformWorld2View(p2);
- Vector tp3 = view->transformWorld2View(p3);
- Vector tp4 = view->transformWorld2View(p4);
-
- /*
- * Front plane clipping (z=0).
- */
- if (tp1[2] < 0 || tp2[2] < 0 || tp3[2] < 0 || tp4[2] < 0)
- return;
-
- Vector normal = (tp2-tp1)*(tp4-tp1);
- if (!normal.normalize())
- return;
-
- Vector lightVector = (tp1+tp2+tp3+tp4)/4;
- calculateColor(color, normal, lightVector);
-
- tp1 = view->transformView2Screen(tp1);
- tp2 = view->transformView2Screen(tp2);
- tp3 = view->transformView2Screen(tp3);
- tp4 = view->transformView2Screen(tp4);
-
- addEdge(tp1, tp2);
- addEdge(tp2, tp3);
- addEdge(tp3, tp4);
- addEdge(tp4, tp1);
-
- render();
- }
-
- void Z_Buffer::renderPolygon(const Color& color, Polygon* p)
- {
- numPrimitives++;
-
- /*
- * Degenerate polygon?
- */
- if (p->numVertices() < 3)
- return;
-
- /*
- * Transform polygon to view space.
- */
- p->transform(view->getViewmat());
-
- /*
- * Front plane clipping (z=0).
- */
- for (register long i=0; i<p->numVertices(); i++)
- if (p->vertex(i)[2] < 0)
- return;
-
- Vector normal = p->normal();
- if (normal.zero())
- return;
-
- Vector lightVector;
- for (i=0; i<p->numVertices(); i++)
- lightVector += p->vertex(i);
- lightVector /= p->numVertices();
-
- calculateColor(color, normal, lightVector);
-
- /*
- * Add each edge of the polygon in the y-bucket structure.
- */
- Vector p1 = view->transformView2Screen(p->vertex(0));
- Vector p2;
- for (i=1; i<p->numVertices(); i++) {
- p2 = view->transformView2Screen(p->vertex(i));
- addEdge(p1, p2);
- p1 = p2;
- }
- p2 = view->transformView2Screen(p->vertex(0));
- addEdge(p1, p2);
-
- render();
- delete p;
- }
-
- void Z_Buffer::writePixmap()
- {
- fprintf(outfile, "P6\n");
- fprintf(outfile, "#%s\n", (const char*) remark);
- fprintf(outfile, "%d\n%d\n255\n", resX, resY);
- fwrite((char*)pixmap, resX*resY*3, 1, outfile);
- fclose(outfile);
- }
-
- void Z_Buffer::addEdge(const Vector& p1, const Vector& p2)
- {
- const Vector& start = (p1[1] <= p2[1]) ? p1 : p2;
- const Vector& end = (p1[1] <= p2[1]) ? p2 : p1;
-
- int y1 = (int)(start[1]);
- int y2 = (int)(end[1]);
-
- /*
- * Horizontal edge, do nothing.
- */
- if (y1 == y2)
- return;
-
- if (y1 < ymin) ymin = y1;
- if (y2 > ymax) ymax = y2;
-
- /*
- * Set up increments.
- */
- real x = start[0];
- real z = start[2];
- real denom = 1/(end[1]-start[1]);
- real dx = (end[0]-x)*denom;
- real dz = (end[2]-z)*denom;
-
- EdgeElement edgeElement;
-
- for (register int y=y1; y<y2; y++, x+=dx, z+=dz) {
-
- /*
- * No clipping is done, so we have to check for out of screen space.
- */
- if (y<0) continue;
- if (y>=resY) break;
-
- /*
- * Create new EdgeElement entry in the Edgelist sorted by x.
- */
- edgeElement.x = x;
- edgeElement.z = z;
- register long i=0;
- while (1) {
- if (i>=yBuckets[y]->count()) {
- yBuckets[y]->append(edgeElement);
- break;
- }
- if (yBuckets[y]->item(i).x > x) {
- yBuckets[y]->insert(i, edgeElement);
- break;
- }
- i++;
- }
- }
- }
-
- void Z_Buffer::render()
- {
- int x1, x2, offset;
- real dx, dz, z;
- unsigned char* pixmapPos;
-
- if (ymin < 0) ymin = 0;
- if (ymax > resY) ymax = resY;
-
- for (register int y=ymin; y < ymax; y++) {
- EdgeList* yBucket = yBuckets[y];
-
- offset = (resY-y)*resY;
- for (register long i=0; i < yBucket->count(); i+=2) {
- x1 = (int)(yBucket->item(i).x);
- x2 = (int)(yBucket->item(i+1).x);
-
- if (x1 != x2) {
- dx = yBucket->item(i+1).x - yBucket->item(i).x;
- dz = (yBucket->item(i+1).z - yBucket->item(i).z)/dx;
- z = yBucket->item(i).z + (yBucket->item(i).x-x1)*dz;
-
- for (register int x=x1; x < x2; x++) {
- if (x >= 0 && x < resX) {
- pixmapPos = pixmap + 3*(offset+x);
- if (z < zBuffer[offset+x]) {
- zBuffer[offset+x] = z;
- *pixmapPos++ = (unsigned char)(R*255);
- *pixmapPos++ = (unsigned char)(G*255);
- *pixmapPos = (unsigned char)(B*255);
- }
- }
- z += dz;
- }
- }
- }
- yBucket->remove_all();
- }
-
- ymin = resY;
- ymax = 0;
- }
-
- void Z_Buffer::calculateColor(const Color& color, const Vector& normal, const Vector& p)
- {
- /*
- * we don't do back face culling, so take absolute value of dot(light,normal).
- * our light sits at (0,0,0) = eye point (=> light vector = (0,0,0) - p)
- */
- Vector toEye = -p; toEye.normalize();
- float intensity = fabs(toEye^normal);
-
- /*
- * we don't calculate the intensity vor each vertex and take an
- * average intensity => as a result the color ist usually too dark
- * => add an ambient term.
- */
- intensity += 0.25; // add an ambient term, because intenisty
- if (intensity>1) intensity = 1;
-
- R = color.r()*intensity;
- G = color.g()*intensity;
- B = color.b()*intensity;
- }
-